home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 011 / device3.arc / DEVICE3.PAS < prev   
Encoding:
Pascal/Delphi Source File  |  1986-03-28  |  15.6 KB  |  548 lines

  1. program Manipulate_Device_Driver_Chain(input,output);
  2. {*********************************************************************
  3.  * DEVICE.PAS             program for displaying device driver chain *
  4.  *                        for DOS 2.00,2.10,3.00,3.10                *
  5.  *                                                                   *
  6.  * by Tim MacNary         july 13,1985                               *
  7.  *                        Turbo Pascal v. 2.00B PC-DOS               *
  8.  *                                                                   *
  9.  * Adapted from a Lattice C program by Stan Mitchell, published in   *
  10.  * Dr. Dobb's Journal, #103 May, 1985, page 122.                     *
  11.  * Please keep this comment here.                                    *
  12.  *                                                                   *
  13.  * Modification history:                                             *
  14.  *    october 30,1985     Fixed a stupid bug in the Hex_Output       *
  15.  *                        routine; it (of course) produced wrong     *
  16.  *                        numbers if the high (sign) bit was set.    *
  17.  *                        That was never a problem with this         *
  18.  *                        program, because device drivers are        *
  19.  *                        always in low memory, but if used in       *
  20.  *                        another program, who knows?                *
  21.  *                        Sorry - Tim MacNary                        *
  22.  *    january 12,1986     Modified program so that device drivers    *
  23.  *                        can be removed from the chain; this will   *
  24.  *                        not remove them from memory, but they      *
  25.  *                        will disappear as far as the operating     *
  26.  *                        system is concerned.                       *
  27.  *                        - Tim MacNary                              *
  28.  *********************************************************************
  29.     This routine uses fields of a standard FCB that Microsoft, in
  30.  it's wisdom, declined to make public. Contained in each opened FCB
  31.  are a Segment:Offset pair that point to the device driver used to
  32.  access the opened file: if you open a disk file, then the driver
  33.  interface to the disk drives is used; if the CON: device, then
  34.  the console driver is used.
  35.     DOS keeps track of the drivers by means of a linked list. Each
  36.  driver has a header area which defines what that device can do, it's
  37.  name, where it's entry points are, and the address of the next driver
  38.  in the list. There is one special driver in the list: the NUL: device.
  39.  It is always at the beginning of the list, so all other drivers will
  40.  follow it.
  41.      The routine to find the chain is as follows:
  42.  
  43. begin
  44.    Determine what DOS version being used
  45.    Exit if the version = 0 ( means dos 1.xx )
  46.    Initialize an FCB with the NUL: device name.
  47.    Open the device; exit if error.
  48.    Get the pointers from the FCB; the pointers are in different places
  49.      for DOS 2 and 3.
  50.    Set up the screen --make it look nice
  51.    Repeat
  52.       Output the header
  53.       Get the next header
  54.    Until the next header offset field = $FFFF
  55.    Output the last header
  56.    Finish the screen display
  57. end
  58.  
  59. }
  60.  
  61. const
  62.  
  63.    { DOS Function codes }
  64.    OpenFCB  = $0F00;
  65.    CloseFCB = $1000;
  66.    DOS_Version  = $3000;
  67.  
  68. type
  69.    DevHdr = record
  70.       Next_Hdr_Offs,
  71.       Next_Hdr_Seg,
  72.       Attributes,
  73.       Strategy,
  74.       Interrupt:integer;
  75.       Dev_Name:array[1..8] of char;
  76.    end;
  77.  
  78.    DevHdr_Ptr = ^DevHdr;
  79.  
  80.    { The next two record types are used to access the pointers in
  81.      the FCB }
  82.  
  83.    Reserve_V2 = record
  84.       time: integer;
  85.       attribute : byte;
  86.       device_header_offset, device_header_segment: integer;
  87.       Unknown : array[1..3] of byte;
  88.    end;
  89.  
  90.    Reserve_V3 = record
  91.       time: integer;
  92.       attribute : integer;
  93.       device_header_offset, device_header_segment: integer;
  94.       Unknown : array[1..2] of byte;
  95.    end;
  96.  
  97.    NameType = array[1..11] of char;
  98.  
  99.    FCB_Type = record
  100.       drive:byte;
  101.       fname:NameType;
  102.       current_block :integer;
  103.       record_size: integer;
  104.       file_size: array[1..2] of integer;
  105.       date: integer;
  106.       RSU: array[1..10] of byte; { This is where the device pointer is stored }
  107.       bset : array[1..5] of byte;
  108.    end;
  109.  
  110.  
  111. var
  112.    device : DevHdr_Ptr;
  113.    Version,Minor:integer;
  114.    ch:char;
  115.  
  116. procedure Init_FCB(Drive:byte;Name:NameType;var File_Control_Block:FCB_Type);
  117.  
  118. { Fill in the Drive and File fields of the FCB.
  119.   Returns an initialized File Control Block. }
  120.  
  121. begin
  122.    File_Control_Block.Drive:=Drive;
  123.    File_Control_Block.FName:=Name;
  124. end; { Init_FCB }
  125.  
  126. procedure Open_Device(var File_Control_Block:FCB_Type;var Error:integer);
  127.  
  128. { The equivalent of either a reset or a rewrite in Turbo Pascal }
  129.  
  130. var  Regs: record
  131.              AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags:integer;
  132.           end;
  133. begin
  134.    Regs.DS:=SEG(File_Control_Block);
  135.    Regs.DX:=OFS(File_Control_Block);
  136.    Regs.AX:=OpenFCB;
  137.    MSDOS(Regs);
  138.    Error:=LO(Regs.AX);
  139. end;
  140.  
  141.  
  142. procedure Hex_Output(Value:integer);
  143.  
  144. { Convert value to a hex string and output it, right-justified in a
  145.   4 character field. }
  146.  
  147. function HexDigit(val:byte):char;
  148. { converts the low 4 bits to a hex number }
  149. type
  150.    _hextype =string[16];
  151. const
  152.    _hex : _hextype = '0123456789ABCDEF';
  153. var
  154.    ch:char;
  155. begin
  156.    ch:=_hex[(val and $0F) + 1];
  157.    HexDigit:=ch;
  158. end;
  159.  
  160. var
  161.      OutStr:string[4];
  162.      High, Low:byte;
  163. begin
  164.    OutStr:='';
  165.  
  166.    High:= Hi(Value);
  167.    Low:= Lo(Value);
  168.  
  169.    OutStr:=HexDigit(high shr 4);
  170.    OutStr:=OutStr + HexDigit(high);
  171.  
  172.    OutStr:=OutStr + HexDigit(low shr 4);
  173.    OutStr:=OutStr + HexDigit(low);
  174.  
  175.    write(OutStr);
  176.  
  177. end { Hex_Output };
  178.  
  179. procedure Print_Header(Dev:DevHdr_Ptr);
  180.  
  181. { Print a device driver header }
  182.  
  183. type Str4=string[4];
  184. var  Co,Co2:integer;
  185.  
  186. procedure WriteIfEqual(Attributes,Mask:integer;Str:Str4;var Co:integer);
  187.  
  188. { If an attribute is present, then print out a 4 character attribute indicator. }
  189.  
  190. begin
  191.    if Attributes AND Mask <> 0 then
  192.    begin
  193.       write(Str);
  194.       Co:=Co + 1
  195.    end
  196. end;
  197.  
  198. begin
  199.    Co:=0;
  200.    write('│ ');
  201.    Hex_Output(SEG(Dev^));
  202.    write('    │');
  203.    Hex_Output(OFS(Dev^));
  204.    write('   │ ');
  205.    WITH Dev^ DO
  206.    begin
  207.       if (Attributes AND $8000) = 0000 then { Block device }
  208.       begin
  209.          write('# Drives:');
  210.          { write out number of drives }
  211.          write(ORD(Dev_Name[1]):2);
  212.          write(' │         ');
  213.       end
  214.       else begin
  215.          WriteIfEqual(Attributes,$0001,'StI ',Co);
  216.          WriteIfEqual(Attributes,$0002,'StO ',Co);
  217.          WriteIfEqual(Attributes,$0004,'Nul ',Co);
  218.          WriteIfEqual(Attributes,$0008,'Clk ',Co);
  219.          WriteIfEqual(Attributes,$0010,'Spl ',Co);
  220.          WriteIfEqual(Attributes,$4000,'IOC ',Co);
  221.          for Co2 := 1 TO (3-Co) do write('    ');
  222.          write('│ ');
  223.          for Co:=1 TO 8 DO write(Dev_Name[Co]); { Character device }
  224.       end;
  225.       write(' │');
  226.       Hex_Output(Strategy);
  227.       write('     │');
  228.       Hex_Output(Interrupt);
  229.       write('      │')
  230.    end;
  231.    writeln
  232. end;
  233.  
  234. procedure Get_DOS_Version(var Major,Minor:integer);
  235.  
  236. { Call MS-DOS to get the dos version number. The two returned values should
  237.   be displayed: write(Major:1,'.',Minor:2);  Dos 1.xx will return a major
  238.   version number of 0. }
  239.  
  240. var  Regs: record
  241.              AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags:integer;
  242.           end;
  243. begin
  244.    Regs.AX:=DOS_Version;
  245.    MSDOS(Regs);
  246.    Major:=LO(Regs.AX);
  247.    Minor:=HI(Regs.AX);
  248. end;
  249.  
  250. procedure Get_Head_of_Device_Chain(var Version,Minor:integer;
  251.                                    var device:devHdr_Ptr);
  252. { Get nul: header location by Opening it; the FCB has fields containing
  253.   the SEG:OFS of the NUL device. }
  254. var
  255.    File_Control_Block : FCB_Type;
  256.    Error:integer;
  257.    rsv2_x: ^reserve_V2;
  258.    rsv3_x: ^reserve_V3;
  259.  
  260. begin
  261.    Init_FCB(0,'NUL        ',File_Control_Block);
  262.    Open_Device(File_Control_Block,Error);
  263.  
  264.    if Error = 0 then   { Nul device opened successfully }
  265.    begin
  266.       case Version of { DOS 2.xx and 3.xx allocate the FCB differently }
  267.          2: { DOS 2.xx }
  268.             begin
  269.                rsv2_x:=PTR(SEG(File_Control_Block),
  270.                            OFS(File_Control_Block)+22);
  271.                Device:=PTR(rsv2_x^.Device_Header_Segment,
  272.                            rsv2_x^.Device_Header_Offset);
  273.             end;
  274.          3: { DOS 3.xx }
  275.             begin
  276.                rsv3_x:=PTR(SEG(File_Control_Block),
  277.                            OFS(File_Control_Block)+22);
  278.                Device:=PTR(rsv3_x^.Device_Header_Segment,
  279.                            rsv3_x^.Device_Header_Offset);
  280.             end;
  281.          else begin
  282.             writeln('DOS Version ',Version:2,'.',Minor:2,' not supported.');
  283.             halt;
  284.          end;
  285.       end; { case }
  286.    end { if }
  287.    else begin
  288.       writeln('Error Opening Nul: device; error=',Error:1,'.');
  289.       HALT
  290.    end
  291. end;
  292.  
  293.  
  294. procedure Set_Up_Screen(Version,Minor:integer);
  295.  
  296. { Write out the column headers, etc }
  297.  
  298. begin
  299.    clrScr;
  300.    write(  '                      ');
  301.    TEXTCOLOR(BLACK);
  302.    TEXTBACKGROUND(WHITE);
  303.    writeln('╔═════════════════════╗');
  304.    TEXTCOLOR(WHITE);
  305.    TEXTBACKGROUND(BLACK);
  306.    write(  '                      ');
  307.    TEXTCOLOR(BLACK);
  308.    TEXTBACKGROUND(WHITE);
  309.    writeln('║ Device Driver Chain ║');
  310.    writeln('╒═════════════════════╩═════════════════════╩═══════════════════╕');
  311.    writeln('│ DOS Version ',Version:1,'.',Minor:2,'                                              │');
  312.    writeln('├───────────────────────────────────────────────────────────────┤');
  313.    writeln('│ Segment  Offset  Attributes    Name       Strategy  Interrupt │');
  314.    writeln('├─────────┬───────┬─────────────┬──────────┬─────────┬──────────┤');
  315. end; { Set_Up_Screen }
  316.  
  317. procedure Finish_Screen;
  318. begin
  319.    writeln('├─────────┴───────┴─────────────┴──────────┴─────────┴──────────┤');
  320.    writeln('│ StI=Standard Input  StO=Standard Output  Nul=Nul Device       │');
  321.    writeln('│ Spl=Special  Clk=Clock  IOC=Input/Output Control              │');
  322.    writeln('└───────────────────────────────────────────────────────────────┘');
  323.    TEXTCOLOR(White);
  324.    TEXTBACKGROUND(Black);
  325. end; { Finish_Screen }
  326.  
  327. procedure Display_Device_Chain(version,minor:integer;
  328.                                device:devHdr_Ptr);
  329. var temp:devHdr_Ptr;
  330. begin
  331.    temp:=device;
  332.    Set_Up_Screen(Version,Minor);
  333.    repeat { loop down the device chain }
  334.  
  335.       Print_Header(temp);
  336.  
  337.       { Get next header location }
  338.       temp:=PTR(temp^.Next_Hdr_Seg,temp^.Next_Hdr_Offs);
  339.    until ( temp^.Next_Hdr_Offs = $FFFF );    { Until last Header }
  340.  
  341.    Print_Header(temp);
  342.    Finish_Screen;
  343.    read(kbd);
  344. end;
  345.  
  346. procedure display_device_names(device:devHdr_Ptr);
  347. var
  348.    temp:devhdr_Ptr;
  349.    co:integer;
  350. begin
  351.    temp:=device;
  352.    repeat
  353.       for co:=1 to 8 do
  354.          write(temp^.dev_Name[co]);
  355.       write('  ');
  356.       if WhereX >=69 then writeln;
  357.       temp:=ptr(temp^.Next_Hdr_Seg,temp^.Next_Hdr_Offs);
  358.    until (temp^.Next_Hdr_Offs = $FFFF );    { Until last Header }
  359.  
  360.    for co:=1 to 8 do
  361.       write(temp^.dev_Name[co]);
  362.    writeln;
  363. end;
  364.  
  365. procedure input_device_name(var name:nametype);
  366. var
  367.    co:integer;
  368.    ch:char;
  369. begin
  370.    for co:= 1 to 11 do
  371.       name[co]:=' ';
  372.  
  373.    co:=0;
  374.    ch:=' ';
  375.    while (co < 8) and (ch <> #13) do
  376.    begin
  377.       read(kbd,ch);
  378.       if (ch=#8) then
  379.       begin
  380.          if (co > 0) then
  381.          begin
  382.             co:=co-1;
  383.             write(ch);
  384.             write(' ');
  385.             write(ch)
  386.          end
  387.       end
  388.       else if ch=#13 then writeln
  389.       else begin
  390.          ch:=upcase(ch);
  391.          write(ch);
  392.          co:=co+1;
  393.          name[co]:=ch;
  394.       end
  395.    end;
  396.  
  397.    writeln;
  398. end;
  399.  
  400. procedure Find_Header(var Device,Temp:devHdr_Ptr;Name:nametype);
  401. var
  402.    co:integer;
  403.    found:boolean;
  404.    temp2:devHdr_Ptr;
  405. begin
  406.    temp2:=device;
  407.    temp:=nil;
  408.    found:=false;
  409.    repeat
  410.       if temp <> nil then { not first time }
  411.       begin
  412.          temp:=temp2;
  413.          temp2:=PTR(temp2^.next_hdr_seg,temp2^.next_hdr_offs);
  414.       end;
  415.  
  416.       co:=1;
  417.       while (name[co]=temp2^.dev_name[co]) and (co <=8) do
  418.       begin
  419.          writeln(name[co],'=',temp2^.dev_name[co]);
  420.          co:=co+1;
  421.       end;
  422.  
  423.       if co=9 then { complete match }
  424.          found:=true;
  425.  
  426.       if (temp=nil) and not found then temp:=temp2;
  427.  
  428.    until found or (temp2^.Next_Hdr_Offs = $FFFF );    { Until last Header }
  429.    if not found then temp:=nil
  430.    else writeln('Found');
  431. end;
  432.  
  433. procedure change_device_name(var device:devHdr_Ptr);
  434. var
  435.    name,replace_name:nametype;
  436.    temp,temp2:devHdr_Ptr;
  437.    co:integer;
  438. begin
  439.    display_device_names(device);
  440.  
  441.    writeln('Input name of driver to change.');
  442.    writeln('You will be prompted "Do you wish to change this header (Y/N) -> ",');
  443.    write  ('after you input the driver name -> [        ]');
  444.    gotoXY(whereX-9,whereY);
  445.  
  446.    input_device_name(name);
  447.    write('[');
  448.    for co:=1 to 8 do
  449.       write(Name[co]);
  450.    writeln(']');
  451.    write('New name -> [        ]');
  452.    gotoXY(whereX-9,whereY);
  453.    input_device_name(replace_name);
  454.  
  455.    write('Do you wish to change this header (Y/N) -> ');
  456.    read(kbd,ch); writeln(ch);
  457.    if upcase(ch)='Y' then
  458.    begin
  459.       { Find the header }
  460.       Find_Header(device,temp,name);
  461.  
  462.       if temp <> nil then
  463.       begin
  464.          temp2:=PTR(temp^.next_hdr_seg,temp^.next_hdr_Offs);
  465.          for co:=1 to 8 do
  466.             temp2^.dev_name[co]:=replace_name[co];
  467.          writeln('Changed ...');
  468.       end
  469.       else writeln('Invalid Device')
  470.    end
  471.    else writeln('Aborted');
  472. end;
  473.  
  474. procedure Remove_Device_Driver(Device:devHdr_Ptr);
  475. { Remove a selected device driver from the chain.
  476.  
  477.   WARNING:WARNING:WARNING:WARNING:WARNING:WARNING
  478.  
  479.      THIS WILL NOT REMOVE THE DEVICE DRIVER
  480.      FROM MEMORY, NOR WILL IT TURN OFF ANY
  481.      INTERRUPTS THE DRIVER MAY HAVE
  482.      INITIALIZED
  483. }
  484.  
  485. var
  486.    name:nametype;
  487.    temp,temp2:devHdr_Ptr;
  488.    co:integer;
  489.  
  490.  
  491. begin
  492.    display_device_names(device);
  493.  
  494.    writeln('Input 8 chars indicating driver to remove, including any blanks.');
  495.    writeln('You will be prompted "Do you wish to delete this header (Y/N) -> ",');
  496.    write  ('after you input the driver name -> [        ]');
  497.    gotoXY(whereX-9,whereY);
  498.  
  499.    input_device_name(name);
  500.    write('[');
  501.    for co:=1 to 8 do
  502.       write(Name[co]);
  503.    writeln(']');
  504.  
  505.    write('Do you wish to delete this header (Y/N) -> ');
  506.    read(kbd,ch); writeln(ch);
  507.    if upcase(ch)='Y' then
  508.    begin
  509.       { Find the header }
  510.       Find_Header(device,temp,name);
  511.  
  512.       if temp <> nil then
  513.       begin
  514.          temp2:=PTR(temp^.next_hdr_seg,temp^.next_hdr_Offs);
  515.  
  516.          temp^.next_hdr_seg:=temp2^.next_hdr_seg;
  517.          temp^.next_hdr_offs:=temp2^.next_hdr_offs;
  518.          writeln('Removed ...');
  519.       end
  520.       else writeln('Invalid Device')
  521.    end
  522.    else writeln('Aborted');
  523. end;
  524.  
  525. begin
  526.    Get_DOS_Version(Version,Minor);
  527.    if Version = 0 then { DOS 1.xx used }
  528.    begin
  529.       writeln('MS-DOS 2.xx or 3.xx required; press any key to continue ...');
  530.       repeat until KEYPRESSED;
  531.       HALT
  532.    end;
  533.  
  534.    Get_Head_of_Device_Chain(Version,Minor,Device);
  535.    Display_Device_Chain(version,minor,Device);
  536.    repeat
  537.       write('D(isplay, R(emove, C(hange, Q(uit -> ');
  538.       read(kbd,ch); writeln(ch);
  539.       case upcase(ch) of
  540.          'D':Display_Device_Chain(version,minor,Device);
  541.          'R':Remove_Device_Driver(Device);
  542.          'C':Change_Device_Name(Device);
  543.          'Q':writeln('Exiting ...');
  544.          else writeln('Invalid input ');
  545.       end
  546.    until upcase(ch)='Q';
  547. end.
  548.